package gggameserver.games.khet;

import gggameserver.games.Board;
import gggameserver.games.Position;
import gggameserver.games.khet.KhetPiece.Type;

import java.util.ArrayList;

/**
 * Board/Piece <-> string
 * 
 * @author tammeojak
 */
public class KhetCommandFormatter {

	public static final String GAME_STATUS_STARTING = "B";
	public static final String GAME_STATUS_RUNNING = "R";
	public static final String GAME_STATUS_ENDED = "E";

	public static final String GAME_DRAW = "d";
	public static final String PLAYER_COLOR_SILVER = "S";
	public static final String PLAYER_COLOR_RED = "R";

	public static final String GAME_END_GAME_RUNNING = "-";

	public static final String GAME_END_REASON_NORMAL = "G";
	public static final String GAME_END_REASON_TIMEOUT = "T";
	public static final String GAME_END_REASON_ILLEGAL_MOVE = "E";

	public static final String GAME_END_REASON_DETAIL_MOVE_LIMIT_EXCEEDED = "M";
	public static final String GAME_END_REASON_DETAIL_3D_SITUATION = "3";

	public static final String MOVE_NO_MOVE = "-";
	public static final String MOVE_TURN_CLOCKWISE = "C";
	public static final String MOVE_TURN_COUNTERCLOCKWISE = "c";

	public static final String PIECE_TYPE_PYRAMID = "Y";
	public static final String PIECE_TYPE_PHARAON = "P";
	public static final String PIECE_TYPE_ANUBIS = "A";
	public static final String PIECE_TYPE_SCARAB = "C";
	public static final String PIECE_TYPE_SPHINX = "X";

	public static final String MOVE_SHOT_SEPARATOR = "#";
	public static final String SHOT_KILL_SEPARATOR = "X";

	public enum GameStatus {
		Starting, Running, Ended
	};

    public static String getGameCommandAsString(KhetBoard kb) {
        // TODO Auto-generated method stub
        return getGameCommandAsString(kb.isGameOver() ? GameStatus.Ended : GameStatus.Running , kb.getActivePlayerNumber(), kb);
    }

	public static String getGameCommandAsString(GameStatus gstatus,
			int playerNumber, KhetBoard kboard) {
		// final result: R S - 97c 1 26 R00X2 R40A2 R50P2 ... S97X3
		StringBuilder s = new StringBuilder();
		// R ...
		s.append(gameStatusToString(gstatus));
		s.append(" ");
		// ... S ...
		s.append(playerNumberToString(playerNumber));
		s.append(" ");
		// ... - 97c 1 26 R00X2 R40A2 R50P2 ... S97X3
		s.append(getGameStateAsString(kboard));

		return s.toString();
	}

	public static String getMoveCommandAsString(KhetMove move) {
		return getMoveAsString(move, null);
	}

	// private members

	private static String gameStatusToString(GameStatus gstatus) {
		switch (gstatus) {
		case Starting:
			return GAME_STATUS_STARTING;
		case Running:
			return GAME_STATUS_RUNNING;
		case Ended:
			return GAME_STATUS_ENDED;
		default:
			throw new IllegalStateException("Illegal game status: " + gstatus);
		}
	}

	private static String playerNumberToString(int playernr) {
		switch (playernr) {
		case 1:
			return PLAYER_COLOR_SILVER;
		case 2:
			return PLAYER_COLOR_RED;
		default:
			throw new IllegalStateException("Illegal playernr: " + playernr);
		}
	}

	private static String getGameStateAsString(KhetBoard kboard) {
		// <- | m�ngu seis> <- | viimane k�ik> <seni tehtud k�ikude arv>
		// <m�ngulaua kirjeldus>
		// - 97c 1 26 R00X2 R40A2 R50P2 ... S97X3
		StringBuilder s = new StringBuilder();

		// <- | m�ngu seis>
		s.append(getGameResultAsString(kboard));
		// <- | viimane k�ik>
		s.append(" ");
		s.append(getMoveAsString(kboard.getLastMove(), kboard.getLastShot()));
		// <seni tehtud k�ikude arv>
		s.append(" ");
		s.append(kboard.getMovesDone());
		// <m�ngulaua kirjeldus>
		s.append(" ");
		s.append(getPiecesAsString(kboard));

		return s.toString();
	}

	private static String getGameResultAsString(KhetBoard kboard) {
		if (!kboard.isGameOver())
			return GAME_END_GAME_RUNNING;

		StringBuilder s = new StringBuilder();

		// winner
		if (kboard.getWinner() == Board.RESULT_DRAW)
			s.append(GAME_DRAW);
		else
			s.append(playerNumberToString(kboard.getWinner()));

		// reason
		if (Board.GameEndReason.TIMEOUT == kboard.getGameEndReason()) {
			s.append(GAME_END_REASON_TIMEOUT);
		} else if (Board.GameEndReason.ILLEGAL_MOVE == kboard
				.getGameEndReason()) {
			s.append(GAME_END_REASON_ILLEGAL_MOVE);
		} else if (Board.GameEndReason.NORMAL == kboard.getGameEndReason()) {
			s.append(GAME_END_REASON_NORMAL);
			if (KhetBoard.GameEndReasonDetails.MOVE_LIMIT_EXCEEDED == kboard
					.getGameEndReasonDetails()) {
				s.append(GAME_END_REASON_DETAIL_MOVE_LIMIT_EXCEEDED);
			} else if (KhetBoard.GameEndReasonDetails.SITUATION_3D == kboard
					.getGameEndReasonDetails()) {
				s.append(GAME_END_REASON_DETAIL_3D_SITUATION);
			} else if (KhetBoard.GameEndReasonDetails.NORMAL == kboard
					.getGameEndReasonDetails()) {
				;
			} else {
				throw new IllegalStateException(
						"Unhandled detailed game end state "
								+ kboard.getGameEndReasonDetails());
			}
		} else {
			throw new IllegalStateException("Unhandled game end state "
					+ kboard.getGameEndReason());
		}

		return s.toString();
	}

	private static String getMoveAsString(KhetMove move, KhetShotResult shot) {
		if (null == move)
			return MOVE_NO_MOVE;

		StringBuilder s = new StringBuilder();

		s.append(playerNumberToString(move.getPlayerNumber()));
		s.append(move.getX());
		s.append(move.getY());

		if (move.getAction() == KhetMove.ACTION_TURN_CLOCKWISE)
			s.append(MOVE_TURN_CLOCKWISE);
		else if (move.getAction() == KhetMove.ACTION_TURN_COUNTERCLOCKWISE)
			s.append(MOVE_TURN_COUNTERCLOCKWISE);
		else
			s.append(move.getAction()); // TODO check value?

		// shot
		if (null != shot) {
			s.append(MOVE_SHOT_SEPARATOR);
			s.append(getShotAsString(shot));
		}

		return s.toString();
	}

	private static String getShotAsString(KhetShotResult shot) {
		StringBuilder s = new StringBuilder();

		final ArrayList<Position> trajectory = shot.getTrajectory();
		if (trajectory.size() == 0)
			throw new IllegalStateException("Trajectory length is 0");

		// starting position
		Position start = trajectory.get(0);
		s.append(start.getX());
		s.append(start.getY());

		// trajectory
		for (int i = 1; i < trajectory.size(); ++i) {
			s.append(getShotDirection(trajectory.get(i - 1), trajectory.get(i)));
		}

		if (null != shot.getDestroyedPiece()) {
			s.append(SHOT_KILL_SEPARATOR);
			s.append(getPieceAsString(shot.getDestroyedPiece()));
		}

		return s.toString();
	}

	private static int getShotDirection(Position p0, Position p1) {
		int dx = p1.getX() - p0.getX();
		int dy = p1.getY() - p0.getY();

		if (0 == dx && -1 == dy)
			return 0;
		if (0 == dx && 1 == dy)
			return 2;
		if (-1 == dx && 0 == dy)
			return 3;
		if (1 == dx && 0 == dy)
			return 1;

		throw new IllegalStateException("Can't find shot direction p0=" + p0
				+ " p1=" + p1);
	}

	public static String getPiecesAsString(KhetBoard kboard) {
		StringBuilder s = new StringBuilder();
		int pieceCnt = 0;

		for (int j = 0; j < KhetBoard.BOARD_HEIGHT; ++j)
			for (int i = 0; i < KhetBoard.BOARD_WIDTH; ++i) {
				KhetPiece p = kboard.getPieceAt(i, j);
				if (null != p && p.isAlive()) {
					s.append(" ");
					s.append(getPieceAsString(p));
					++pieceCnt;
				}
			}

		s.insert(0, pieceCnt);
		return s.toString();
	}

	private static String getPieceAsString(KhetPiece piece) {
		StringBuilder s = new StringBuilder();
		// S97X3
		s.append(playerNumberToString(piece.getPlayerNumber()));
		s.append(piece.getX());
		s.append(piece.getY());
		s.append(pieceTypeToString(piece.getType()));
		s.append(piece.getDirection());

		return s.toString();
	}

	private static String pieceTypeToString(Type type) {
		switch (type) {
		case PYRAMID:
			return PIECE_TYPE_PYRAMID;
		case PHARAON:
			return PIECE_TYPE_PHARAON;
		case ANUBIS:
			return PIECE_TYPE_ANUBIS;
		case SCARAB:
			return PIECE_TYPE_SCARAB;
		case SPHINX:
			return PIECE_TYPE_SPHINX;
		default:
			throw new IllegalStateException("Illegal piece type: " + type);
		}
	}

}
